<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
        <meta
            name="viewport"
            content="width=device-width, minimal-ui, viewport-fit=cover, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"
        />
        <link rel="icon" type="image/png" href="assets/favicon.png" />

        <title>OGL • Post FXAA (Fast Approximate Anti-Aliasing)</title>
        <link href="assets/main.css" rel="stylesheet" />
    </head>
    <body>
        <div class="Info">Post FXAA (Fast Approximate Anti-Aliasing)</div>
        <script type="module">
            import { Renderer, Camera, Program, Mesh, Vec2, Post, Box, NormalProgram } from '../src/index.mjs';

            const fragment = /* glsl */ `
                precision highp float;

                // Default uniform for previous pass is 'tMap'.
                // Can change this using the 'textureUniform' property
                // when adding a pass.
                uniform sampler2D tMap;

                uniform vec2 uResolution;

                varying vec2 vUv;

                vec4 fxaa(sampler2D tex, vec2 uv, vec2 resolution) {
                    vec2 pixel = vec2(1) / resolution;

                    vec3 l = vec3(0.299, 0.587, 0.114);
                    float lNW = dot(texture2D(tex, uv + vec2(-1, -1) * pixel).rgb, l);
                    float lNE = dot(texture2D(tex, uv + vec2( 1, -1) * pixel).rgb, l);
                    float lSW = dot(texture2D(tex, uv + vec2(-1,  1) * pixel).rgb, l);
                    float lSE = dot(texture2D(tex, uv + vec2( 1,  1) * pixel).rgb, l);
                    float lM  = dot(texture2D(tex, uv).rgb, l);
                    float lMin = min(lM, min(min(lNW, lNE), min(lSW, lSE)));
                    float lMax = max(lM, max(max(lNW, lNE), max(lSW, lSE)));
                    
                    vec2 dir = vec2(
                        -((lNW + lNE) - (lSW + lSE)),
                        ((lNW + lSW) - (lNE + lSE))
                    );
                    
                    float dirReduce = max((lNW + lNE + lSW + lSE) * 0.03125, 0.0078125);
                    float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);
                    dir = min(vec2(8, 8), max(vec2(-8, -8), dir * rcpDirMin)) * pixel;
                    
                    vec3 rgbA = 0.5 * (
                        texture2D(tex, uv + dir * (1.0 / 3.0 - 0.5)).rgb +
                        texture2D(tex, uv + dir * (2.0 / 3.0 - 0.5)).rgb);

                    vec3 rgbB = rgbA * 0.5 + 0.25 * (
                        texture2D(tex, uv + dir * -0.5).rgb +
                        texture2D(tex, uv + dir * 0.5).rgb);

                    float lB = dot(rgbB, l);

                    return mix(
                        vec4(rgbB, 1),
                        vec4(rgbA, 1),
                        max(sign(lB - lMin), 0.0) * max(sign(lB - lMax), 0.0)
                    );
                }

                void main() {
                    vec4 raw = texture2D(tMap, vUv);
                    vec4 aa = fxaa(tMap, vUv, uResolution);

                    // Split screen in half to show side-by-side comparison
                    gl_FragColor = mix(raw, aa, step(0.5, vUv.x));

                    // Darken left side a tad for clarity
                    gl_FragColor -= step(vUv.x, 0.5) * 0.1;
                }
            `;

            {
                const renderer = new Renderer({ dpr: 1 });
                const gl = renderer.gl;
                document.body.appendChild(gl.canvas);
                gl.clearColor(1, 1, 1, 1);

                const camera = new Camera(gl, { fov: 35 });
                camera.position.set(0, 1, 5);
                camera.lookAt([0, 0, 0]);

                // Post copies the current renderer values (width, height, dpr) if none are passed in
                const post = new Post(gl);

                // Create uniform for pass
                const resolution = { value: new Vec2() };

                function resize() {
                    renderer.setSize(window.innerWidth, window.innerHeight);
                    camera.perspective({ aspect: gl.canvas.width / gl.canvas.height });

                    // Need to resize post as render targets need to be re-created
                    post.resize();
                    resolution.value.set(gl.canvas.width, gl.canvas.height);
                }
                window.addEventListener('resize', resize, false);
                resize();

                const geometry = new Box(gl);
                const mesh = new Mesh(gl, { geometry, program: new NormalProgram(gl) });

                // Add pass like you're creating a Program. Then use the 'enabled'
                // property to toggle the pass.
                const pass = post.addPass({
                    // If not passed in, pass will use the default vertex/fragment
                    // shaders found within the class.
                    fragment,
                    uniforms: {
                        uResolution: resolution,
                    },
                });

                requestAnimationFrame(update);
                function update() {
                    requestAnimationFrame(update);

                    mesh.rotation.y -= 0.005;
                    mesh.rotation.x -= 0.01;

                    // Replace Renderer.render with post.render. Use the same arguments.
                    post.render({ scene: mesh, camera });
                }
            }
        </script>
    </body>
</html>
